﻿/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2017 @ ShenZhen ,China
*******************************************************************************/
#include "Tracer.h"
#include "MemShared.h"

#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>        /* For mode constants */
#include <fcntl.h>           /* For O_* constants */
       
MemShared::MemShared(int type):
m_shmSize(0),
m_shmAddr(NULL),
m_shmType(type)
{
}
MemShared::~MemShared()
{
}
bool MemShared::open(const char *name, int ioMode)
{
    if (NULL==name ||
		(ioMode < 0) ||
		(ioMode > IO_MODE_APPEND_ONLY))
	{
		TRACE_ERR_CLASS("Parameter error.\n");
		return false;
	}
    
    if (m_fd >= 0)
	{
		TRACE_ERR_CLASS("Device is already opened!\n");
		return false;
	}
    if(m_shmType==MEMSHARED_TYPE_SHM)
	{
    	switch(ioMode)
    	{
    		case IO_MODE_RD_ONLY:
    			m_fd = ::shm_open(name, O_RDONLY,0666);
    			break;
    		case IO_MODE_RDWR_ONLY:
    			m_fd = ::shm_open(name, O_RDWR|O_CREAT,0666);
    			break;
    		default:
    			TRACE_ERR_CLASS("Unsupport SHM IO Mode: %d\n",ioMode);
    			return false;
    	}
    }
    else
    {
        switch(ioMode)
    	{
    		case IO_MODE_RD_ONLY:
    			m_fd = ::open(name, O_RDONLY,0666);
    			break;
            case IO_MODE_WR_ONLY:
    			m_fd = ::open(name, O_WRONLY|O_CREAT,0666);
    			break;
    		case IO_MODE_RDWR_ONLY:
    			m_fd = ::open(name, O_RDWR|O_CREAT,0666);
    			break;
    		default:
    			TRACE_ERR_CLASS("Unsupport FILE IO Mode : %d\n",ioMode);
    			return false;
    	}
    }
    
	if (m_fd<0)
	{
		TRACE_ERR_CLASS("Open %s error: %s\n",name,ERROR_STRING);
		return false;
	}
    
    if (-1==ftruncate(m_fd,m_shmSize))
    {
        TRACE_ERR_CLASS("shm[%s] set size(%d) error:%s!\n",m_devName.c_str(),m_shmSize,ERROR_STRING);
        return false;    
    }
    struct stat fdStat;
    if (-1==fstat(m_fd,&fdStat))
    {
        TRACE_ERR_CLASS("shm[%s] fstat error:%s!\n",m_devName.c_str(),ERROR_STRING);
        return false;
    }
    m_shmSize=fdStat.st_size;
    m_devName = name;
    m_openMode = ioMode;
	return true;  
}

bool MemShared::close()
{
    if (m_shmType==MEMSHARED_TYPE_SHM)
    {
        if (shm_unlink(m_devName.c_str())!=0)
        {
            TRACE_ERR_CLASS("shm[%s] unlink error:%s!\n",m_devName.c_str(),ERROR_STRING);
            return false;
        }
    }
    else
    {
        if ((m_fd>0)&&(::close(m_fd)!=0))
        {
            TRACE_ERR_CLASS("shm file[%s] close error:%s!\n",m_devName.c_str(),ERROR_STRING);
            return false;
        }
    }
    return true;
}

int MemShared::setAttribute(int attr, int value)
{
    if (m_fd >=0 )
	{
		TRACE_ERR_CLASS("Device has been opened,can't set attribute!\n");
		return STATUS_ERROR;
	}
    switch(attr)
    {
        case MEMSHARED_ATTR_SIZE:
            m_shmSize=value;
            break;
        default:
            TRACE_ERR_CLASS("Unsupport attr:%d.\n",attr);
            return STATUS_ERROR;
    }
}
int MemShared::getAttribute(int attr)
{
    struct stat fdStat;
    if (m_fd<0)
    {
        TRACE_ERR_CLASS("Device has not been opened,can't get attribute!\n");
		return STATUS_ERROR;
    }
    switch(attr)
    {
        case MEMSHARED_ATTR_SIZE:
            if (-1==fstat(m_fd,&fdStat))
            {
                TRACE_ERR_CLASS("shm[%s] fstat error:%s!\n",m_devName.c_str(),ERROR_STRING);
                return STATUS_ERROR;
            }
            m_shmSize=fdStat.st_size;
            return m_shmSize;
        default:
            TRACE_ERR_CLASS("Unsupport attr:%d.\n",attr);
            return STATUS_ERROR;
    }
}
/**
 *  \brief  创建并绑定共享内存
 *  \param  none
 *  \return 成功返回内存地址,失败返回NULL
 *  \note   none
 */
void* MemShared::attach()
{    
    void* addr = mmap(NULL,m_shmSize,PROT_READ|PROT_WRITE,MAP_SHARED,m_fd,0);
    if (MAP_FAILED==addr)
    {
        TRACE_ERR_CLASS("shm[%s] map error:%s!\n",m_devName.c_str(),ERROR_STRING);
        return NULL;    
    }
    return m_shmAddr=addr;
}

/**
 *  \brief  销毁共享内存区域
 *  \param  none
 *  \return 成功返回STATUS_OK,失败返回STATUS_ERROR
 *  \note   none
 */
int MemShared::detach()
{
    if (munmap(m_shmAddr,m_shmSize)!=0)
    {
        return STATUS_ERROR;   
    }
    return STATUS_OK;
}

int MemShared::readData(char *buf, int count, int timeoutMs)
{
    return STATUS_ERROR;
}
int MemShared::writeData(const char *buf, int count, int timeoutMs)
{
    return STATUS_ERROR;
}

